// basic_profile.h

// These classes are only to help defining the neccecary interfaces.
// We are going to make the core functions TEMPLATE, so any class that
// looks like these ones should work.
//


#ifndef DODO_BASIC_PROFILE_H
#define DODO_BASIC_PROFILE_H
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <stdexcept>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/lexical_cast.hpp>

#include <quigon/basic/constants.h>
#include <quigon/basic/basic_utils.h>
#include <quigon/basic/math_utils.h>
namespace quigon
{
template < class T1 = double, class T2 = double >
class Profile
{
public:
	typedef T1 value_type;
	typedef T2 attr_type;
	typedef typename std::vector<value_type>::iterator iterator;
	typedef typename std::vector<value_type>::const_iterator const_iterator;
	typedef typename std::map<std::string, attr_type>::const_iterator attr_iterator;


	Profile() : value(), attr() {};
	Profile(size_t size0) : value(size0), attr() {};

	bool empty() const
	{
		return value.empty();
	}
	const size_t size() const
	{
		return value.size();
	}
	bool resize(size_t size0, value_type val0 = value_type())
	{
		value.resize(size0, val0);
		return true;
	}
	void clear()
	{
		value.clear();
		attr.clear();
	}
	void clear_value()
	{
		value.clear();
	}
	void clear_attr()
	{
		attr.clear();
	}
	void reset_value()
	{
		fill(value.begin(), value.end(), 0);
	}
	void reset_attr()
	{
		for (typename std::map<std::string, attr_type>::iterator ai = attr.begin(); ai != attr.end(); ++ai)
		{
			ai->second = 0;
		}
	}
	iterator begin()
	{
		return value.begin();
	}
	iterator end()
	{
		return value.end();
	}
	const_iterator begin() const
	{
		return value.begin();
	}
	const_iterator end() const
	{
		return value.end();
	}

	attr_iterator attr_begin() const
	{
		return attr.begin();
	}
	attr_iterator attr_end() const
	{
		return attr.end();
	}
	bool has_attr(const std::string& s) const
	{
		if (attr.find(s) != attr.end())
			return true;
		else
			return false;
	}

	value_type& operator [](size_t i)
	{
		return value[i];
	}
	const value_type& operator [](size_t i) const
	{
		return value[i];
	}
	attr_type& operator [](const std::string& attr_name)
	{
		return attr[attr_name];
	}
	const attr_type operator [](const std::string& attr_name) const
	{
		typename std::map<std::string, attr_type>::const_iterator iter = attr.find(attr_name);
		if (iter != attr.end())
			return iter->second;
		else
			throw std::invalid_argument(std::string("There's no attr_name: ") + attr_name);
	}

	bool save(const std::string& filename, const std::string& note = "", const std::string& time_format = "%Y-%m-%d %H:%M:%S") const
	{
		try
		{
			std::ofstream file(filename.c_str());
			if (!file)
			{
				return false;
			}
			// set to default time format
			imbue(file, time_format);

			file << "# " << filename << std::endl;
			print_time(file, "# file updated at: ");
			if (note != "")
				file << "# " << note << std::endl;

			file << attr.size() << std::endl;
			for (typename std::map<std::string, attr_type>::const_iterator iter = attr.begin();
			     iter != attr.end();
			     ++iter)
			{
				file << iter->first << "\t" << iter->second << std::endl;
			}
			for (size_t i = 0; i < value.size(); ++i)
			{
				file << value[i] << std::endl;
			}
			return true;
		}
		catch (...)
		{
			func_err("Profile::save", filename);
			throw;
		}
	}

	bool load(const std::string& filename, const std::string& time_format = "%Y-%m-%d %H:%M:%S")
	{
		try
		{
			std::ifstream file(filename.c_str());
			if (!file)
			{
				return false;
			}

			std::string templine;
			std::stringstream ss;
			imbue(ss, time_format);

			std::string attr_name;
			attr_type attr_value;
			value_type temp_value;

			std::getline(file, templine);    // comment line
			while (templine.length() == 0 || templine[0] == '#')
				std::getline(file, templine);
			// get until not a comment line, then it will be attr_count
			size_t attr_count = boost::lexical_cast<size_t> (templine);
			attr.clear();

			for (size_t i = 0; i < attr_count; ++i)
			{
				std::getline(file, templine);
				ss.clear();
				ss << templine;
				ss >> attr_name >> attr_value;
				attr[attr_name] = attr_value;
			}

			value.clear();
			while (std::getline(file, templine) && !file.eof())
			{
				if (templine.length() == 0 || templine[0] == '#')
					break;
				temp_value = boost::lexical_cast<value_type> (templine);
				value.push_back(temp_value);
			}
			return true;
		}
		catch (...)
		{
			func_err("Profile::load", filename);
			throw;
		}
	}
protected:
	std::vector<value_type> value;
	std::map<std::string, attr_type> attr;
};

//Double is faster than float.
template < class T1 = double, class T2 = double >
class BasicLidarLine : public Profile<T1, T2>
{
public:
	BasicLidarLine()
			: Profile<T1, T2>(),
			lidar_name_(),
			status_(Status::INVALID),
			t_(boost::posix_time::not_a_date_time),
			duration_()
	{ }
	BasicLidarLine(size_t size0)
			: Profile<T1, T2> (size0) ,
			lidar_name_(),
			status_(Status::INVALID),
			t_(boost::posix_time::not_a_date_time),
			duration_()
	{ }

	bool is_valid() const
	{
		return !(this->status() & Status::INVALID);
	}
	virtual bool operator == (const BasicLidarLine<T1, T2>& line) const
	{
		return	(lidar_name() == line.lidar_name()) &&
		       (t() == line.t()) &&
		       (duration() == line.duration()) &&
		       (status() == line.status()) ;
	}
	virtual bool operator < (const BasicLidarLine<T1, T2>& line) const
	{
		return t() < line.t();
	}
	typename boost::posix_time::ptime& t()
	{
		return t_;
	}
	const typename boost::posix_time::ptime& t() const
	{
		return t_;
	}
	typename boost::posix_time::time_duration& duration()
	{
		return duration_;
	}
	const typename boost::posix_time::time_duration& duration() const
	{
		return duration_;
	}
	virtual int& status()
	{
		return status_;
	}
	virtual const int& status() const
	{
		return status_;
	}
	std::string& lidar_name()
	{
		return lidar_name_;
	}
	const std::string& lidar_name() const
	{
		return lidar_name_;
	}

protected:
	boost::posix_time::ptime t_;
	boost::posix_time::time_duration duration_;
	int status_;
	std::string lidar_name_;
};

typedef Profile<double, double> Lidar_Profile;
}

#endif

